/*
 * @Author: weidewei
 * @Date: 2025-11-03 10:00:15
 * @LastEditors: weidewei
 * @LastEditTime: 2025-11-03 13:40:11
 * @Description:
 * @FilePath: \2025-front-face\js常见手写题\deepClone.js
 */

/*
1.基础类型直接返回​
2.循环引用检测​
3.特殊对象处理​
4.创建空对象/数组​
5.记录当前对象
6.递归拷贝属性​
7.处理 Symbol 键​
*/
/* ​​循环引用​​：必须用 WeakMap记录已拷贝对象，否则会栈溢出。
​​特殊对象​​：Date、RegExp、Map、Set等需要特殊处理。
​​Symbol 键​​：普通 for...in循环无法遍历，需要用 Object.getOwnPropertySymbols()。
​​性能优化​​：WeakMap比 Map更合适（弱引用，不影响垃圾回收）。
​​函数处理​​：通常不深拷贝函数（保持原引用）。 */
function deepClone(sourceObj, map = new WeakMap()) {
  // 基础类型直接返回​
  if (sourceObj === null || typeof sourceObj !== "object") return sourceObj;
  // 处理循环引用（已拷贝过的对象直接返回）
  if (map.has(sourceObj)) {
    return map.get(sourceObj);
  }
  // 处理日期对象
  if (sourceObj instanceof Date) {
    return new Date(sourceObj.getTime());
  }
  // 处理正则对象
  if (sourceObj instanceof RegExp) {
    return new RegExp(sourceObj);
  }
  // 处理Map
  if (sourceObj instanceof Map) {
    const cloneMap = new Map();
    map.set(sourceObj, cloneMap);
    sourceObj.forEach((value, key) => {
      clonedMap.set(deepClone(key, map), deepClone(value, map));
    });
    return cloneMap;
  }
  // 处理set
  if (sourceObj instanceof Set) {
    const cloneSet = new Set();
    map.set(sourceObj, cloneSet);
    sourceObj.forEach((value) => {
      clonedSet.add(deepClone(value, map));
    });
    return clonedSet;
  }
  // 处理数组和对象
  const cloneObj = Array.isArray(sourceObj) ? [] : {};
  // 记录已拷贝对象（解决循环引用）
  map.set(sourceObj, cloneObj);
  // 递归拷贝所有属性
  for (let key in sourceObj) {
    if (sourceObj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(sourceObj[key], map);
    }
  }
  // 处理 Symbol 类型的属性键
  const symbolKeys = Object.getOwnPropertySymbols(sourceObj);
  for (let symKey of symbolKeys) {
    cloneObj[symKey] = deepClone(sourceObj[symKey], map);
  }
  return cloneObj;
}

const obj = {
  name: "xxx",
  age: 20,
  fn: function () {
    return true;
  },
  null1: null,
  dateStr: new Date(),
  arr: [1, 2, 3],
  obj1: {
    a: 111,
    b: 222,
  },
  symbol: Symbol('keyyy')
};
console.log("obj", deepClone(obj));


